﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections.ObjectModel;
using System.IO;

namespace SharedLib.BasicServices.Plugin.Configuration
{
    public class PluginManager
    {
        //#region Singleton
        ///// <summary>
        ///// Get the singleton instance
        ///// </summary>
        //public static PluginManager Instance
        //{
        //    get { return SingletonHelper._instance; }
        //}

        ///// <summary>
        ///// Nested helper class
        ///// </summary>
        //internal class SingletonHelper
        //{
        //    static SingletonHelper() { }
        //    internal static readonly PluginManager _instance = new PluginManager();
        //}

        //private PluginManager()
        //{
        //    //Init codes come here
        //    Initialize();
        //}
        //#endregion

        public PluginManager()
        {
            Initialize();
        }

        private IDictionary<string, Type> _plugins = new Dictionary<string, Type>();
        /// <summary>
        /// Only Registered Types will be loaded
        /// </summary>
        protected IDictionary<string, Type> Plugins
        {
            get { return _plugins; }
        }

        /// <summary>
        /// Get loaded plugin types
        /// </summary>
        public Type[] PluginTypes
        {
            get
            {
               return Plugins.Values.ToArray();
            }
        }
        //private IDictionary<string, IDictionary<string, Type>> _plugins = new Dictionary<string, IDictionary<string, Type>>();

        //protected IDictionary<string, IDictionary<string, Type>> Plugins
        //{
        //    get { return _plugins; }
        //}

        //public void RegisterType<T>() where T:IPlugin
        //{
        //    //foreach (Type typeInterface in pluginType.GetInterfaces())
        //    //{
        //    //    if (typeInterface == typeof(IPlugin))
        //    //    {
            
        //    //        return;
        //    //    }
        //    //}

        //    //    throw new ArgumentException("Type is not implementing IPlugin", "t");
        //    Type t = typeof(T);
        //    if (!RegisteredTypes.Contains(t)) RegisteredTypes.Add(t);
        //}

        /// <summary>
        /// Override for customizd plugin folders
        /// </summary>
        public virtual string getPluginFolder()
        {
            return "Plugins";
        }

        /// <summary>
        /// Override for customizd initialization operations
        /// </summary>
        protected virtual void Initialize()
        {
            //LoadPlugins();
        }

        public virtual void LoadPlugins()
        {
            string folderFullName = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + getPluginFolder();
            if (!Directory.Exists(folderFullName)) Directory.CreateDirectory(folderFullName);
            foreach (string pluginPath in Directory.GetFiles(folderFullName, "*.dll", System.IO.SearchOption.AllDirectories))
            {
                LoadPlugin(pluginPath);
            }
        }

        /// <summary>
        /// Load plugins from path
        /// </summary>
        /// <param name="fullPath">plugin dir</param>
        public void LoadPlugin(string fullPath)
        {
            Assembly pluginAssembly = Assembly.LoadFile(fullPath);
            Type pluginType = pluginAssembly.GetTypes()[0];
            foreach (Type typeInterface in pluginType.GetInterfaces()) 
            {
                if (typeInterface == typeof(IPlugin))
                {
                    if (!Plugins.ContainsKey(pluginType.FullName))
                    {
                        Plugins.Add(pluginType.FullName, pluginType);
                    }
                }
                //if (RegisteredTypes.Contains(typeInterface))
                //{
                //    IDictionary<string, Type> pluginsInSpecifiedInterface;
                //    string interfaceName = typeInterface.FullName;
                //    if (!Plugins.ContainsKey(interfaceName))
                //    {
                //        pluginsInSpecifiedInterface = new Dictionary<string, Type>();
                //        Plugins.Add(interfaceName, pluginsInSpecifiedInterface);
                //    }
                //    else
                //    {
                //        pluginsInSpecifiedInterface = Plugins[interfaceName];
                //    }
                //    pluginsInSpecifiedInterface.Add(pluginType.FullName, pluginType);
                //} 
            }
        }

        ///// <summary>
        ///// Create plugin instance
        ///// </summary>
        ///// <param name="typeInterface"></param>
        ///// <param name="pluginName"></param>
        ///// <returns></returns>
        //public T CreatePlugin<T>(string pluginName) where T:class, IPlugin
        //{
        //    return CreatePlugin(typeof(T).FullName, pluginName) as T;
        //}

        /// <summary>
        /// Create plugin instance
        /// </summary>
        /// <param name="interfaceName"></param>
        /// <param name="pluginName"></param>
        /// <returns></returns>
        public IPlugin CreatePlugin(string pluginName)
        {
            //if (Plugins.ContainsKey(interfaceName))
            //{
            //    IDictionary<string, Type> pluginsInSpecifiedInterface = Plugins[interfaceName];
            //    if (pluginsInSpecifiedInterface.ContainsKey(pluginName))
            //    {
            //        Type pluginType = pluginsInSpecifiedInterface[pluginName];

            //        return (IPlugin)Activator.CreateInstance(pluginType);
            //    }
            //}
            if (Plugins.ContainsKey(pluginName))
            {
                Type pluginType = Plugins[pluginName];

                return (IPlugin)Activator.CreateInstance(pluginType);
            }
            return null;
        }

        
    }
}
